home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Educational / MolViewer / Source / D3View.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  17.0 KB  |  745 lines

  1. /* 3DView.m   Copyright 1992 Steve Ludtke */
  2. /* This view allows the display of molecules with real time rotation */
  3. /* The quick mode is handled in drawPS:: */
  4.  
  5. #import "Mtypes.h"
  6. #import "D3View.h"
  7. #import "MolObj.h"
  8. #import "MolShape.h"
  9. #import <appkit/appkit.h>
  10. #import <dpsclient/event.h>
  11. #import <dpsclient/psops.h>
  12. #import <stdio.h>
  13. #import <math.h>
  14. #include <libc.h>
  15.  
  16. void PSsethel();    /* makes Helvetica current font */
  17. int comp(struct DSORT *a,struct DSORT *b);
  18.  
  19. #define LRAD 10.0
  20.  
  21. extern id NXApp;
  22.  
  23. @implementation D3View
  24. -initFrame:(NXRect *)myrect
  25. {
  26. char s[120];
  27. RtPoint fromP = {0,0,60.0}, toP = {0,0,0};
  28. RtPoint lFromP = {5.0,10.0,5.0},lToP = {0,0,0};
  29. int i;
  30. id aShader;
  31.  
  32. bpath=(char *)[[NXBundle mainBundle] directory];
  33.  
  34. [super initFrame:(NXRect *)myrect];
  35.  
  36. /* set up light sources, etc... */
  37. [self setBackgroundColor:NX_COLORWHITE];  
  38. [self setDrawBackgroundColor:YES];
  39.  
  40.     edist=60.0;
  41.     [self setEyeAt:fromP toward:toP roll:0.0];
  42.   
  43.   aShader=[[N3DShader alloc] init];
  44.   [(N3DShader *)aShader setShader:"matte"];
  45.  
  46.   shape=[[MolShape alloc] init];
  47.   [(N3DShape *) shape  setShader:aShader];
  48.   [shape scale:-1.0 :1.0 :1.0];
  49.   [[self setWorldShape:shape] free];
  50.  
  51.   ambLight=[[N3DLight alloc] init];
  52.   [ambLight makeAmbientWithIntensity:0.4];
  53.   [self addLight:ambLight];
  54.  
  55.  
  56. ltheta=.785;
  57. lchi=-.785;
  58. aLight=[[N3DLight alloc] init];
  59. lFromP[0]= LRAD*sin(lchi)*cos(ltheta);
  60. lFromP[2]= LRAD*cos(lchi)*cos(ltheta);
  61. lFromP[1]= LRAD*sin(ltheta);
  62. [aLight makeDistantFrom:lFromP to:lToP intensity:1.0];
  63. [self addLight:aLight];
  64.  
  65. Rmode=4;        /* start in "quick" mode */
  66. useColor=0;
  67.  
  68. /* start spinning, all angles in radians */
  69. chi=phi=0;
  70. theta=0;
  71. /*dchi=0.025;*/
  72. dchi=dtheta=0.0;
  73. initflag=1;
  74. Rflags=0;
  75.  
  76. /* printer setup */
  77. [[[NXApp printInfo] setVertCentered:YES] setOrientation:NX_PORTRAIT andAdjust:YES];
  78. [[NXApp printInfo] setMarginLeft:0.0 right:0.0 top:0.0 bottom:0.0];
  79. [[NXApp printInfo] setHorizPagination:NX_FITPAGINATION];
  80. [[NXApp printInfo] setVertPagination:NX_FITPAGINATION];
  81.  
  82. busy=0;
  83.  
  84. /* images for spinning icon */
  85. for (i=0; i<8; i++) {
  86.     sprintf(s,"%s/icon%d.tiff",bpath,i);
  87.     AIMGS[i]=[[NXImage alloc] initFromFile:s];
  88. }
  89.  
  90. timer=(void *)
  91.     DPSAddTimedEntry(TIMESTEP,(void *)itstime,self,NX_RUNMODALTHRESHOLD);
  92.  
  93. minshape=0;
  94. return self;
  95. }
  96.  
  97. - appDidInit: sender
  98. {
  99. AICV=[[NXApp appIcon] contentView];
  100. AIMG=[NXImage findImageNamed: "NXAppTile"];
  101. Ior1.x=Ior1.y=0.0;
  102. Ior2.x=Ior2.y=8.0;
  103.  
  104. animicon=0;
  105.  
  106. [self setSurfaceTypeForAll:N3D_WireFrame chooseHider:YES];
  107. [self zoom:self];
  108. initflag=0; 
  109.  
  110. return self;
  111. }
  112.  
  113. -setData:(Molecule *)Mol :(int)Nmol :(struct ELINFO *)Elinfo
  114. {
  115. mol=Mol;
  116. nmol=Nmol;
  117. elinfo=Elinfo;
  118. [self zoom:self];
  119. return self;
  120. }
  121.  
  122.  
  123. /* fix the scaling after being resized */
  124. -superviewSizeChanged:(const NXSize *)oldsize
  125. {
  126. [super superviewSizeChanged:oldsize];
  127. [self zoom:self];
  128. return self;
  129. }
  130.  
  131. -free
  132. {
  133. DPSRemoveTimedEntry(timer);
  134. [super free];
  135. return self;
  136. }
  137.  
  138. /* This is called after QRM has finished rendering. In quick mode */
  139. /* nothing is rendered. It's all done here */
  140. -drawPS:(NXRect *)rects :(int)rectCount
  141. {
  142. char s[80];
  143. float mx[9];
  144. int i,j,k;
  145. float tx,ty,tz,sca,xc,yc,xw,yw;
  146. static struct DSORT *dsort=NULL;
  147. static int dsmax=1000000;
  148. NXPoint point;
  149. NXSize size;
  150.  
  151. if (mol==NULL) return self;
  152. if (Rmode==4) { 
  153. /* ok, since the QRman interface has a bug, here we'll do some quick stuff */
  154. /* with roughly the same effect (this is also much faster for space-filling) */
  155.     if (mol[0].numa>dsmax) { free(dsort); dsort=NULL; }
  156. /* dsort structure holds drawing information. For space filling mode it */
  157. /* is actually sorted. In stick mode it just holds the transformed */
  158. /* molecule coordinates */
  159.     if (dsort==NULL) {
  160.         dsort=malloc(sizeof(struct DSORT)*(mol[0].numa+1));
  161.         dsmax=mol[0].numa;
  162.     }
  163.  
  164.     PSsetlinewidth(0.0);
  165.     PSsetgray(1.0);
  166.     NXRectFill(&bounds);
  167.  
  168.     PSsetgray(NX_BLACK);
  169.  
  170. /* a transform that almost matches the QRM transformation */
  171.     chi*=-1.0;
  172.     phi*=-1.0;
  173.     theta*=-1.0;
  174.     mx[0]=cos(chi)*cos(phi)-cos(theta)*sin(phi)*sin(chi);
  175.     mx[1]=cos(chi)*sin(phi)+cos(theta)*cos(phi)*sin(chi);
  176.     mx[2]=sin(chi)*sin(theta);
  177.     mx[3]= -sin(chi)*cos(phi)-cos(theta)*sin(phi)*cos(chi);
  178.     mx[4]= -sin(chi)*sin(phi)+cos(theta)*cos(phi)*cos(chi);
  179.     mx[5]=cos(chi)*sin(theta);
  180.     mx[6]=sin(theta)*sin(phi);
  181.     mx[7]= -sin(theta)*cos(phi);
  182.     mx[8]=cos(theta);
  183.     chi*=-1.0;
  184.     phi*=-1.0;
  185.     theta*=-1.0;    
  186.  
  187. /* scaling info */
  188.     xw=bounds.size.width;
  189.     yw=bounds.size.height;
  190.     xc=bounds.size.width/2.0;
  191.     yc=bounds.size.height/2.0;
  192.     sca=bounds.size.height*1.8;
  193.  
  194. /* transform the coordinates -> DSORT struct */
  195.     for (i=0; i<mol[0].numa; i++) {
  196.         tx=mol[0].coord[i][0];
  197.         ty= -mol[0].coord[i][2];
  198.         tz=mol[0].coord[i][1];
  199.  
  200.         dsort[i].c[1]=tx*mx[3]+ty*mx[4]+tz*mx[5]+edist;
  201.         if (dsort[i].c[1]<=0.0) continue;
  202.         dsort[i].c[0]=(tx*mx[0]+ty*mx[1]+tz*mx[2])*sca/dsort[i].c[1]+xc;
  203.         dsort[i].c[2]=(tx*mx[6]+ty*mx[7]+tz*mx[8])*sca/dsort[i].c[1]+yc;
  204.         dsort[i].anum=mol[0].atom[i].anum;
  205.         dsort[i].sel=mol[0].atom[i].sel;
  206.         if (dsort[i].c[0]<0 ||dsort[i].c[0]>xw||dsort[i].c[2]<0|| dsort[i].c[2]>yw) dsort[i].c[0]=-1.0;
  207.     }
  208.  
  209. /* stick mode. Use user paths for speed */
  210.     if (Rflags==0) {
  211.         pathc=0;
  212.         for (i=0; i<mol[0].numlb; i++) {
  213.             j=mol[0].lb[i].n1;
  214.             k=mol[0].lb[i].n2;
  215.             if (dsort[j].c[0]==-1.0||dsort[k].c[0]==-1.0) continue;
  216.             path[pathc*2]=dsort[j].c[0];
  217.             path[pathc*2+1]=dsort[j].c[2];
  218.             path[pathc*2+2]=dsort[k].c[0];
  219.             path[pathc*2+3]=dsort[k].c[2];
  220.             ops[pathc]=dps_moveto;
  221.             ops[pathc+1]=dps_lineto;
  222.             pathc+=2;
  223.         if (pathc>=1400) { 
  224.             DPSDoUserPath(path, pathc * 2, dps_float, ops, pathc, &bounds, 
  225.                 dps_ustroke);
  226.             pathc=0;
  227.         }
  228.         }
  229.         if (pathc!=0) DPSDoUserPath(path, pathc * 2, dps_float, ops, pathc, 
  230.                 &bounds, dps_ustroke);
  231.  
  232.         sca*=1.05;
  233.         sca/=edist;        
  234.         /* circle selected atoms */
  235.         for (i=0; i<mol[0].numa; i++) {
  236.             if (dsort[i].sel) {
  237.                 PSsetgray(0.0);
  238.                 PSmoveto(dsort[i].c[0]+sca/3.0,dsort[i].c[2]);
  239.                 PSarc(dsort[i].c[0],dsort[i].c[2],sca/3.0,0.0,360.0);
  240.                 PSstroke();
  241.             }
  242.         }
  243.     }
  244. /* space-filling and ball-stick mode are the same in quick mode */
  245.     else {
  246.         sca*=1.05;
  247.         sca*=(elinfo[0].rad/elinfo[0].arad);
  248.         sca/=edist;        
  249.         size.height=size.width=floor(sca)*2.0;
  250.  
  251.         /* scale the pre-rendered images. I tried using RIB images */
  252.         /* here, but it gave all sorts of errors. NeXT said that it */
  253.         /* should work. Maybe next release ... */
  254.         for (i=0; i<16; i++) {
  255.             if (elinfo[i].image==nil) continue;
  256.             [elinfo[i].image setSize:&size];
  257.         }
  258.         /* Sort for painters algorithm */
  259.         qsort(dsort,mol[0].numa,sizeof(struct DSORT),(void *)comp);
  260.         
  261.         /* composite images */
  262.         for (i=0; i<mol[0].numa; i++) {
  263.             if (dsort[i].c[1]<0.0) continue;
  264.             j=dsort[i].anum;
  265.             point.x=dsort[i].c[0]-sca;
  266.             point.y=dsort[i].c[2]-sca;
  267.             [elinfo[j].image composite:NX_SOVER toPoint:&point];
  268.             /* put a dot on selected atoms (or circle if H) */
  269.             if (dsort[i].sel) {
  270.                 PSmoveto(dsort[i].c[0]+sca/3.0,dsort[i].c[2]);
  271.                 PSarc(dsort[i].c[0],dsort[i].c[2],sca/3.0,0.0,360.0);
  272.                 if (j==0) {
  273.                     PSsetgray(0.0);
  274.                     PSstroke();
  275.                 } else {
  276.                     PSsetgray(1.0);
  277.                     PSfill();
  278.                 }
  279.             }
  280.         }
  281.     }
  282. }        
  283.  
  284. if (bounds.size.width<150) { busy=0; return self; }
  285. /* display alt and az in upper right corner */
  286. PSsetgray(0.0);
  287. PSsethel();
  288. sprintf(s,"Alt:%6.2f  Az:%6.2f",theta*57.295787,chi*57.295787);
  289. PSmoveto(bounds.size.width-100.0,bounds.size.height-15.0);
  290. PSshow(s);
  291. PSstroke();
  292.  
  293. busy=0;
  294. return self;
  295. }    
  296.  
  297. /* recalculates and redisplays data */
  298. -zoom:sender
  299. {
  300. [shape setData:mol :nmol :elinfo :Rmode :Rflags];
  301. [shape setAng:theta :chi :phi];
  302. [self display];
  303.  
  304. return self;
  305. }
  306.  
  307. /* obvious */
  308. -setAng:(float)az :(float)alt :(float)Phi
  309. {
  310. theta=alt;
  311. chi=az;
  312. phi=Phi;
  313. return self;
  314. }
  315.  
  316. /* allows spinning and zooming with the mouse */
  317. -mouseDown:(NXEvent *)oevent 
  318. {
  319. int oldMask,loop=1;
  320. float ix=0,iy=0,ix1=0,iy1=0,ix2=0,iy2=0,ix3=0,iy3=0;
  321. NXEvent *event,evs;
  322. long tm,tm2;
  323.  
  324. evs=*oevent;
  325. oevent=&evs;
  326. [self convertPoint:&oevent->location fromView:nil];
  327. oevent->location.x=oevent->location.x/bounds.size.width*2.0-1.0;
  328. oevent->location.y=oevent->location.y/bounds.size.height*2.0-1.0;
  329. ix2=ix=oevent->location.x;
  330. iy2=iy=oevent->location.y;
  331. tm2=tm=oevent->time;
  332.  
  333. oldMask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];
  334.  
  335. while (loop) {
  336.     event = [NXApp getNextEvent:(NX_LMOUSEUPMASK | NX_LMOUSEDRAGGEDMASK)];
  337.     [self convertPoint:&event->location fromView:nil];
  338.     event->location.x=event->location.x/bounds.size.width*2.0-1.0;
  339.     event->location.y=event->location.y/bounds.size.height*2.0-1.0;
  340. /*    [shape setDrawAsBox:YES];*/
  341.         switch (event->type) {
  342.         case NX_LMOUSEUP:
  343.                 loop = 0;
  344.                 if (event->time-tm2==0) tm2=event->time-1;
  345.                 dchi=(event->location.x-ix2)/(float)(event->time-tm2)*15.0;
  346.                 dtheta=-(event->location.y-iy2)/(float)(event->time-tm2)*15.0;
  347.                 if (fabs(dchi)<.004) dchi=0.0;
  348.                 [shape setAng:theta :chi :phi];
  349.         /*        [shape setDrawAsBox:NO];*/
  350.                 [self display];
  351.             break;
  352.         case NX_LMOUSEDRAGGED:
  353.             theta-=(event->location.y-iy)*3.0;
  354.             chi+=(event->location.x-ix)*4.0;
  355.             if (theta>M_PI) theta-=2.0*M_PI;
  356.             if (theta<-M_PI) theta+=2.0*M_PI;
  357.             while (chi>2.0*M_PI) chi-=2.0*M_PI;
  358.             while (chi<0.0) chi+=2.0*M_PI;
  359.             ix2=ix;
  360.             iy2=iy;
  361.             tm2=tm;
  362.             ix=event->location.x;
  363.             iy=event->location.y;
  364.             tm=event->time;
  365.             [shape setAng:theta :chi :phi];
  366.             [self display];
  367.             break;
  368.         default:
  369.             break;
  370.         }
  371. }
  372. [window setEventMask:oldMask];
  373. return self;
  374. }
  375.  
  376. /* function called by timer */
  377. void itstime(DPSTimedEntry entry,double now,id call)
  378. {
  379. [call step];
  380. return;
  381. }
  382.  
  383. /* do one time step */
  384. -step
  385. {
  386. if (initflag) { 
  387.     /* first time, do initialization stuff */
  388. }
  389. /* This animates the icon */
  390. if (animicon>=0 && (dchi!=0 || dtheta!=0)) {
  391.     [AICV lockFocus];
  392.     [AIMG composite:NX_SOVER toPoint:&Ior1];                                                           
  393.     [AIMGS[animicon] composite:NX_SOVER toPoint:&Ior2];                                                           
  394.     [AICV unlockFocus];
  395.     [AICV display];
  396.     animicon++;
  397.     if (animicon>7) animicon=0;
  398. }
  399.  
  400. if (minshape) {
  401.     [controller minStep:self];
  402.     if (dchi==0.0) {
  403.         [self display];
  404.         return self;
  405.     }
  406. }
  407. if (dchi==0.0&&dtheta==0.0) return self;
  408. chi+=dchi;
  409. theta+=dtheta;
  410. if (theta>M_PI) theta-=2.0*M_PI;
  411. if (theta<-M_PI) theta+=2.0*M_PI;
  412. while (chi>2.0*M_PI) chi-=2.0*M_PI;
  413. while (chi<0.0) chi+=2.0*M_PI;
  414. if (busy) {
  415.     return self;
  416. }
  417. [shape setAng:theta :chi :phi];
  418. [self display];
  419. return self;
  420. }
  421.  
  422. -(int)acceptsFirstMouse { return (YES); }
  423.  
  424. -setcontroller:con 
  425. {
  426. controller=con;
  427. return self;
  428. }
  429.  
  430. /* allow user to pause spinning (not used)*/
  431. -togFreeze:sender
  432. {
  433. static float Tdchi;
  434.  
  435. if ([sender intValue]) {
  436.     Tdchi=dchi;
  437.     dchi=0.0;
  438.     return self;
  439. }
  440. dchi=Tdchi;
  441. return self;
  442. }
  443.  
  444. /* just set a few things before MolShape takes over */
  445. - renderSelf:(RtToken)context
  446. {
  447. RtFloat par[2]= {8.0,8.0};
  448. /*RiErrorIgnore();*/
  449.  
  450. RiShadingRate(2.0);
  451. RiGeometricApproximation(RI_TESSELATION,RI_PARAMETRIC,par,RI_NULL);
  452. busy=1;
  453. return self;
  454. }
  455.  
  456. /* dump the current screen image as a RIB file */
  457. - dumpRib:sender
  458. {
  459.   static id savePanel=nil;
  460.   NXStream *ts;
  461.   char buf[MAXPATHLEN+1];
  462.   int omode;
  463.  
  464.   if (!savePanel) {
  465.     savePanel=[SavePanel new];
  466.     [savePanel setRequiredFileType:"rib"];
  467.   }
  468.  
  469.   if([savePanel runModal]){
  470.     ts=NXOpenMemory(NULL, 0, NX_WRITEONLY);
  471.     strcpy(buf, [savePanel filename]);
  472.     strrchr(buf,'.')[0]='\0';
  473.     NXPrintf(ts, "Display \"%s.tiff\" \"file\" \"rgb\"\n", buf);
  474.     omode=Rmode;
  475.     Rmode=3;
  476. /* the Rmode+8 causes a white rectangle to appear behind the molecule */
  477.     [shape setData:mol :nmol :elinfo :Rmode+8 :2];
  478.     [self copyRIBCode:ts];
  479.     Rmode=omode;
  480.     [shape setData:mol :nmol :elinfo :Rmode :Rflags];
  481.     NXSaveToFile(ts, [savePanel filename]);
  482.     NXCloseMemory(ts,NX_FREEBUFFER);
  483.   }
  484.   return self;
  485. }
  486.  
  487. - setAmbLight:sender
  488. {
  489. [ambLight setIntensity:[sender floatValue]];
  490. [self display];
  491. return self;
  492. }
  493.  
  494. - setLight:sender
  495. {
  496. [aLight setIntensity:[sender floatValue]];
  497. [self display];
  498. return self;
  499. }
  500.  
  501. -setLightX:sender
  502. {
  503. RtPoint from;
  504. lchi= -[sender floatValue];
  505. from[0]= LRAD*sin(lchi)*cos(ltheta);
  506. from[2]= LRAD*cos(lchi)*cos(ltheta);
  507. from[1]= LRAD*sin(ltheta);
  508. [aLight setFrom:from];
  509. [self display];
  510. return self;
  511. }
  512.  
  513. -setLightY:sender
  514. {
  515. RtPoint from;
  516. ltheta=[sender floatValue];
  517. from[0]= LRAD*sin(lchi)*cos(ltheta);
  518. from[2]= LRAD*cos(lchi)*cos(ltheta);
  519. from[1]= LRAD*sin(ltheta);
  520. [aLight setFrom:from];
  521. [self display];
  522. return self;
  523. }
  524.  
  525. /* rendering mode - ie. point,line,surface,smooth,quick ... */
  526. - setMode:sender
  527. {
  528. int i;
  529.  
  530. Rmode=i=[sender selectedRow];
  531.  
  532. [shape setData:mol :nmol :elinfo :Rmode :Rflags];
  533. switch(i) {
  534. case 0:
  535.  [self setSurfaceTypeForAll:N3D_PointCloud chooseHider:YES];
  536.  if (!useColor) [window setDepthLimit:NX_TwoBitGrayDepth];
  537. break;
  538. case 1:
  539.  [self setSurfaceTypeForAll:N3D_WireFrame chooseHider:YES];
  540.  if (!useColor) [window setDepthLimit:NX_TwoBitGrayDepth];
  541. break;
  542. case 2:
  543.  [self setSurfaceTypeForAll:N3D_FacetedSolids chooseHider:YES];
  544. break;
  545. case 3:
  546.  [self setSurfaceTypeForAll:N3D_SmoothSolids chooseHider:YES];
  547. break;
  548. case 4:
  549.  [self setSurfaceTypeForAll:N3D_WireFrame chooseHider:NO];
  550.  [self setHider:N3D_NoRendering];
  551.  if (!useColor) [window setDepthLimit:NX_TwoBitGrayDepth];
  552. break;
  553. }
  554. [self display];
  555. return self;
  556. }
  557.  
  558. -printPS:sender
  559. {
  560. [shape setData:mol :nmol :elinfo :Rmode+8 :Rflags];
  561. [super printPSCode:sender];
  562. [shape setData:mol :nmol :elinfo :Rmode :Rflags];
  563. return self;
  564. }
  565.  
  566. /* mode, ie. stick, ball&stick or spacefill */
  567. -setFlags:sender
  568. {
  569. Rflags=0;
  570. if ([[flagSel findCellWithTag:1] intValue]) Rflags+=1;
  571. if ([[flagSel findCellWithTag:2] intValue]) Rflags+=2;
  572. [shape setData:mol :nmol :elinfo :Rmode :Rflags];
  573. [self display];
  574. return self;
  575. }
  576.  
  577. /* dist form observer to object */
  578. -setEDist:sender
  579. {
  580. RtPoint fromP = {0,0,30.0}, toP = {0,0,0};
  581.  
  582. edist= -[sender floatValue];
  583. fromP[2]=edist;
  584. [self setEyeAt:fromP toward:toP roll:0.0];
  585. [self display];
  586. return self;
  587. }
  588.  
  589. -setPhi:sender
  590. {
  591. phi=[sender floatValue]/57.2958;
  592. [shape setAng:theta :chi :phi];
  593. [self display];
  594. return self;
  595. }
  596.  
  597. /* generate a sequence of RIB's as an animation */
  598. - ribRot:sender
  599. {
  600. static id savePanel=nil;
  601. NXStream *ts;
  602. float ophi;
  603. char buf[MAXPATHLEN+1],s[20];
  604. int i,omode;
  605.  
  606. if (savePanel==nil) savePanel=[SavePanel new];
  607. [savePanel setRequiredFileType:"rib"];
  608.   
  609. ophi=phi;
  610. omode=Rmode;
  611. if([savePanel runModal]){
  612. /* if you want to change the number of frames in the animation, change */
  613. /* the next 2 lines */
  614.     for (i=1; i<=30; i+=1) {
  615.         phi=(float)i*.2094;
  616.         [shape setAng:theta :chi :phi];
  617.         strcpy(buf, [savePanel filename]);
  618.         strrchr(buf,'.')[0]='\0';
  619.         ts=NXOpenMemory(NULL, 0, NX_WRITEONLY);
  620.         NXPrintf(ts, "Display \"%s.%d.tiff\" \"file\" \"rgb\"\n", buf,i);
  621.         Rmode=3;
  622.         [shape setData:mol :nmol :elinfo :Rmode :2]; 
  623.         [self copyRIBCode:ts];
  624.         Rmode=omode;
  625.         [shape setData:mol :nmol :elinfo :Rmode :Rflags];
  626.         [self display];
  627.         sprintf(s,".%d.rib",i);
  628.         strcat(buf,s);
  629.         NXSaveToFile(ts, buf);
  630.         NXCloseMemory(ts,NX_FREEBUFFER);
  631.     }
  632. }
  633. phi=ophi;
  634. [shape setAng:theta :chi :phi];
  635. [self display];
  636. return self;
  637. }
  638.  
  639. /* This used to dump a DKB raytracer format file. Now it dumps a POV */
  640. /* (based on DKB) file. You can get POV from wuarchive.wustl.edu */
  641. /* in /graphics/graphics/ray. POV is certainly slower than renderman, */
  642. /* but it is much more flexible. */
  643. -dkbDump:sender
  644. {
  645. static id savePanel=nil;
  646. FILE *out;
  647. char s[80];
  648. int i,n;
  649. float x,y,z,mx[9];
  650.  
  651. if (savePanel==nil) savePanel=[SavePanel new];
  652. [savePanel setRequiredFileType:"pov"];
  653.   
  654. if([savePanel runModal]){
  655.     sprintf(s,"cp %s/header.dat %s",bpath,[savePanel filename]);
  656.     system(s);
  657.     out=fopen([savePanel filename],"a");
  658.     fprintf(out,"camera {\n    location <0 0 %f>\n",edist);
  659.     fprintf(out,"    up <0 1 0>\n    right <1.33333 0 0>\n");
  660.     fprintf(out,"    look_at <0 0 0>\n    sky <0 1 0>\n}\n");
  661.  
  662.     chi*=-1.0;
  663.     phi*=-1.0;
  664.     theta*=-1.0;
  665.     mx[0]=cos(chi)*cos(phi)-cos(theta)*sin(phi)*sin(chi);
  666.     mx[1]=cos(chi)*sin(phi)+cos(theta)*cos(phi)*sin(chi);
  667.     mx[2]=sin(chi)*sin(theta);
  668.     mx[3]= -sin(chi)*cos(phi)-cos(theta)*sin(phi)*cos(chi);
  669.     mx[4]= -sin(chi)*sin(phi)+cos(theta)*cos(phi)*cos(chi);
  670.     mx[5]=cos(chi)*sin(theta);
  671.     mx[6]=sin(theta)*sin(phi);
  672.     mx[7]= -sin(theta)*cos(phi);
  673.     mx[8]=cos(theta);
  674.     chi*=-1.0;
  675.     phi*=-1.0;
  676.     theta*=-1.0;    
  677.  
  678.     for (i=0; i<mol[0].numa; i++) {
  679.         x=mol[0].coord[i][0];
  680.         y=mol[0].coord[i][1];
  681.         z=mol[0].coord[i][2];
  682.         n=mol[0].atom[i].anum;
  683.  
  684.         fprintf(out,"object {\n    sphere { <%f %f %f> %f }\n", 
  685.             x*mx[0]+y*mx[1]+z*mx[2],x*mx[3]+y*mx[4]+z*mx[5],
  686.             x*mx[6]+y*mx[7]+z*mx[8],elinfo[n].rad);
  687.  
  688.         fprintf(out,"    texture {\n\tcolor red %5.3f green %5.3f blue %5.3f\n\tphong 1.0\n    }\n}\n\n",elinfo[n].color[0], elinfo[n].color[1],elinfo[n].color[2]);
  689.  
  690.     }
  691.     fclose(out);
  692. }
  693. return self;
  694. }
  695.  
  696. -togMin:sender
  697. {
  698. if ([sender intValue]) minshape=1;
  699. else minshape=0;
  700.  
  701. return self;
  702. }
  703.  
  704. /* save the current scene as EPS */
  705. - dumpEPS:sender
  706. {
  707. static id savePanel=nil;
  708. NXStream *out;
  709.  
  710. if (!savePanel) {
  711.     savePanel=[SavePanel new];
  712.     [savePanel setRequiredFileType:"eps"];
  713. }
  714.  
  715. if([savePanel runModal]){
  716.     out=NXOpenMemory(NULL,0, NX_WRITEONLY);
  717.     [self copyPSCodeInside:&bounds to:out];
  718.     NXSaveToFile(out,[savePanel filename]);
  719.     NXCloseMemory(out,NX_FREEBUFFER);
  720. }
  721. return self;
  722. }
  723.  
  724. -setColor:sender
  725. {
  726. useColor=[sender intValue];
  727. return self;
  728. }
  729.  
  730. /* NeXT said that this should clean up most of the QRM problems. */
  731. /* As far as I can tell it doesn't have any effect */
  732. - flushRIB
  733. {
  734.     RiSynchronize( RI_FLUSH );
  735.     RiSynchronize( RI_WAIT );
  736.     return self;
  737. }
  738.  
  739. int comp(struct DSORT *a,struct DSORT *b)
  740. {
  741. if (a->c[1]>b->c[1]) return(-1);
  742. else return(1);
  743. }
  744. @end
  745.